#ifndef __EVENT_HPP__
#define __EVENT_HPP__ 1

#include <functional>
#include <queue>
#include <unordered_map>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>
#include <cassert>

#include "Debug.hpp"

#if 0
struct EventListener
{
	EventListener(EventType eventType, std::function<bool(Event*)>&& func) : eventType(eventType), func(func)
	{
	}

	EventType eventType;
	std::function<bool(Event*)> func;

private:
	const EventListener& operator= (const EventListener&) { }
};
#endif

class CWindow;

// Okay this should really just be an opaque GUID/hash of string
enum class EventType : int
{
	Shutdown = 0,
	WindowCreated,
	WindowDestroyed,
	WindowResize,
};

struct EventData
{
	virtual void Destroy() = 0;
	virtual ~EventData() { };
};

struct Shutdown : public EventData
{
	Shutdown(void*) : data(nullptr)
	{
	}

	void Destroy() { delete this; }
	void* data;
};

static std::chrono::milliseconds longest;

struct Event
{
	template<typename T>
	Event(EventType eventType, T* data) : eventType(eventType), data(data)
	{
		startTick = std::chrono::high_resolution_clock::now();
	}
	~Event();

	std::chrono::high_resolution_clock::time_point startTick;
	std::chrono::high_resolution_clock::time_point endTick;

	EventType eventType;
	EventData* data;
};

class EventHandler
{
public:
	typedef std::unordered_multimap<std::underlying_type<EventType>::type, std::function<bool(Event*)>> Listeners;
	typedef std::queue<Event*> Events;

	EventHandler();
	~EventHandler();

	//const EventHandler& operator=(const EventHandler& other) { return other; }

	template<typename T>
	Event& Produce(EventType eventType, T* data)
	{
		auto evt = new Event(eventType, data);

		events.push(evt);

		eventsPending.notify_all();

		return *evt;
	}

	void Consume();
	int EventThread();
	void AddListener(EventType eventType, std::function<bool(Event*)> func);
	void RemoveListener(EventType eventType, std::function<bool(Event*)> func);
	std::unique_lock<std::mutex> GetLock();

private:
	EventHandler(const EventHandler&) { }
	
	bool ready;
	bool shutdown;
	std::condition_variable eventsPending;
	std::mutex mtx;
	std::thread thr;
	Listeners listeners;
	Events events;

};

#endif
